package org.Yiran.timetale_re.worldgen.feature;

import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import org.Yiran.timetale_re.block.BlocksRegister;
import org.Yiran.timetale_re.block.crystal.base.CrystalBaseBlock;
import org.Yiran.timetale_re.worldgen.feature.config.CrystalClusterConfig;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 水晶簇特征类
 * <p>
 * 这个类实现了在世界中生成水晶簇的逻辑。
 * 每个水晶簇包含4-8个水晶，可能包含1-4种不同类型的水晶。
 * 水晶会根据附着面的方向正确朝向，并且有不同等级。
 */
public class CrystalClusterFeature extends Feature<CrystalClusterConfig> {
    
    // 保存世界引用，以便在内部方法中使用
    private WorldGenLevel level;

    public CrystalClusterFeature(Codec<CrystalClusterConfig> codec) {
        super(codec);
    }

    @Override
    public boolean place(FeaturePlaceContext<CrystalClusterConfig> context) {
        this.level = context.level();
        BlockPos origin = context.origin();
        RandomSource random = context.random();
        CrystalClusterConfig config = context.config();
        
        // 检查生成位置是否合适
        if (!level.isEmptyBlock(origin)) {
            return false;
        }
        
        // 获取附着面
        Direction attachDirection = getAttachDirection(level, origin);
        if (attachDirection == null) {
            return false;
        }
        
        // 根据不同环境确定生成概率
        if (!shouldGenerateInEnvironment(origin, config, random)) {
            return false;
        }
        
        // 生成水晶簇
        return generateCrystalCluster(origin, attachDirection, config, random);
    }
    
    /**
     * 根据环境确定是否应该生成
     * 
     * @param pos 生成位置
     * @param config 配置
     * @param random 随机数生成器
     * @return 是否应该生成
     */
    private boolean shouldGenerateInEnvironment(BlockPos pos, CrystalClusterConfig config, RandomSource random) {
        // 简化的环境判断逻辑
        // 实际项目中可能需要更复杂的环境检测逻辑
        int y = pos.getY();
        
        // 根据Y坐标判断环境类型并应用相应概率
        if (y > 80) {
            // 地表环境
            return random.nextDouble() < config.surfaceGenerationChance();
        } else if (y > 40) {
            // 洞穴环境
            return random.nextDouble() < config.caveGenerationChance();
        } else {
            // 大型矿洞环境
            return random.nextDouble() < config.largeCaveGenerationChance();
        }
    }
    
    /**
     * 获取合适的附着面
     * 
     * @param level 世界
     * @param pos 位置
     * @return 附着面，如果找不到合适的附着面则返回null
     */
    private Direction getAttachDirection(WorldGenLevel level, BlockPos pos) {
        // 检查六个方向是否有合适的附着面
        List<Direction> validDirections = new ArrayList<>();
        for (Direction direction : Direction.values()) {
            BlockPos attachPos = pos.relative(direction.getOpposite());
            BlockState attachState = level.getBlockState(attachPos);
            
            // 检查是否是石头或深层板岩
            if (attachState.is(BlockTags.STONE_ORE_REPLACEABLES) || 
                attachState.is(BlockTags.DEEPSLATE_ORE_REPLACEABLES)) {
                validDirections.add(direction);
            }
        }
        
        // 如果有有效的附着面，随机选择一个
        if (!validDirections.isEmpty()) {
            return validDirections.get(0); // 默认返回第一个
        }
        
        return null;
    }
    
    /**
     * 生成水晶簇
     * 
     * @param origin 原点位置
     * @param attachDirection 附着方向
     * @param config 配置
     * @param random 随机数生成器
     * @return 是否生成成功
     */
    private boolean generateCrystalCluster(BlockPos origin, Direction attachDirection, 
                                         CrystalClusterConfig config, RandomSource random) {
        // 确定水晶簇的大小
        int clusterSize = config.minClusterSize() +
                         random.nextInt(config.maxClusterSize() - config.minClusterSize() + 1);
        
        // 确定水晶类型数量（1-4种）
        // 种类越多的水晶簇群生成的概率越高
        int typeCount;
        int typeRand = random.nextInt(100);
        if (typeRand < 40) {
            typeCount = 1; // 40% 概率只有一种类型
        } else if (typeRand < 70) {
            typeCount = 2; // 30% 概率有两种类型
        } else if (typeRand < 90) {
            typeCount = 3; // 20% 概率有三种类型
        } else {
            typeCount = 4; // 10% 概率有四种类型
        }
        
        // 根据权重选择水晶类型
        List<CrystalBaseBlock> selectedTypes = selectCrystalTypes(typeCount, random);
        
        // 生成水晶簇位置
        List<BlockPos> positions = getClusterPositions(origin, clusterSize, random);
        if (positions.isEmpty()) {
            return false;
        }
        
        // 确定峰的数量（1-3个）
        int peakCount = 1 + random.nextInt(3);
        if (peakCount > positions.size()) {
            peakCount = positions.size();
        }
        
        // 选择峰的位置
        List<BlockPos> peaks = new ArrayList<>();
        List<BlockPos> remainingPositions = new ArrayList<>(positions);
        
        // 随机选择峰的位置
        for (int i = 0; i < peakCount; i++) {
            if (!remainingPositions.isEmpty()) {
                int index = random.nextInt(remainingPositions.size());
                peaks.add(remainingPositions.remove(index));
            }
        }
        
        // 生成水晶
        boolean success = false;
        Set<BlockPos> placedPositions = new HashSet<>();
        
        // 先放置峰（高等级水晶）
        for (BlockPos peakPos : peaks) {
            if (level.isEmptyBlock(peakPos) && !placedPositions.contains(peakPos)) {
                // 为每个峰独立选择附着面，增加多样性
                Direction validAttachDirection = getRandomAttachDirection(level, peakPos, random);
                if (validAttachDirection != null) {
                    CrystalBaseBlock crystalBlock = selectedTypes.get(random.nextInt(selectedTypes.size()));
                    // 峰使用较高等级（2-3级）
                    int crystalLevel = 2 + random.nextInt(2);
                    
                    if (placeCrystal(peakPos, validAttachDirection, crystalBlock, crystalLevel)) {
                        success = true;
                        placedPositions.add(peakPos);
                    }
                }
            }
        }
        
        // 放置周围的水晶，根据与峰的距离确定等级
        for (BlockPos pos : remainingPositions) {
            if (level.isEmptyBlock(pos) && !placedPositions.contains(pos)) {
                // 为每个水晶独立选择附着面，增加多样性
                Direction validAttachDirection = getRandomAttachDirection(level, pos, random);
                if (validAttachDirection != null) {
                    CrystalBaseBlock crystalBlock = selectedTypes.get(random.nextInt(selectedTypes.size()));
                    
                    // 根据与最近峰的距离确定等级
                    int crystalLevel = getLevelBasedOnDistance(pos, peaks, random);
                    
                    if (placeCrystal(pos, validAttachDirection, crystalBlock, crystalLevel)) {
                        success = true;
                        placedPositions.add(pos);
                    }
                }
            }
        }
        
        return success;
    }
    
    /**
     * 根据与峰的距离确定水晶等级
     * 
     * @param pos 当前位置
     * @param peaks 峰的位置列表
     * @param random 随机数生成器
     * @return 水晶等级（1-3）
     */
    private int getLevelBasedOnDistance(BlockPos pos, List<BlockPos> peaks, RandomSource random) {
        if (peaks.isEmpty()) {
            return 1 + random.nextInt(3); // 如果没有峰，随机等级
        }
        
        // 计算到最近峰的距离
        double minDistance = Double.MAX_VALUE;
        for (BlockPos peak : peaks) {
            double distance = Math.sqrt(pos.distSqr(peak));
            if (distance < minDistance) {
                minDistance = distance;
            }
        }
        
        // 根据距离确定等级
        // 距离越近等级越高
        if (minDistance <= 1.5) {
            // 最近峰附近，高概率为3级，低概率为2级
            return random.nextInt(4) == 0 ? 2 : 3;
        } else if (minDistance <= 2.5) {
            // 中等距离，高概率为2级，低概率为1级或3级
            int rand = random.nextInt(10);
            if (rand < 7) {
                return 2;
            } else if (rand < 9) {
                return 1;
            } else {
                return 3;
            }
        } else {
            // 较远距离，高概率为1级，低概率为2级
            return random.nextInt(5) == 0 ? 2 : 1;
        }
    }
    
    /**
     * 根据权重选择水晶类型
     * 
     * @param count 选择数量
     * @param random 随机数生成器
     * @return 选中的水晶类型列表
     */
    private List<CrystalBaseBlock> selectCrystalTypes(int count, RandomSource random) {
        List<CrystalBaseBlock> selected = new ArrayList<>();
        
        // 水晶类型及其权重
        // 混沌水晶权重最高
        CrystalBaseBlock[] crystalTypes = {
            (CrystalBaseBlock) BlocksRegister.CHAOS_CRYSTAL.get(),
            (CrystalBaseBlock) BlocksRegister.DIMENSION_CRYSTAL.get(),
            (CrystalBaseBlock) BlocksRegister.SPACE_CRYSTAL.get(),
            (CrystalBaseBlock) BlocksRegister.TIME_CRYSTAL.get()
        };
        
        int[] weights = {4, 1, 1, 1}; // 混沌水晶权重最高
        
        // 创建带权重的选项列表
        List<CrystalBaseBlock> weightedOptions = new ArrayList<>();
        for (int i = 0; i < crystalTypes.length; i++) {
            for (int j = 0; j < weights[i]; j++) {
                weightedOptions.add(crystalTypes[i]);
            }
        }
        
        // 选择指定数量的类型（不重复）
        while (selected.size() < count && !weightedOptions.isEmpty()) {
            CrystalBaseBlock type = weightedOptions.get(random.nextInt(weightedOptions.size()));
            if (!selected.contains(type)) {
                selected.add(type);
            }
        }
        
        // 如果没有选够数量，用随机类型填充
        while (selected.size() < count) {
            CrystalBaseBlock type = crystalTypes[random.nextInt(crystalTypes.length)];
            if (!selected.contains(type)) {
                selected.add(type);
            }
        }
        
        return selected;
    }
    
    /**
     * 获取水晶簇的位置
     * 
     * @param origin 原点
     * @param count 水晶数量
     * @param random 随机数生成器
     * @return 位置列表
     */
    private List<BlockPos> getClusterPositions(BlockPos origin, int count, RandomSource random) {
        List<BlockPos> positions = new ArrayList<>();
        positions.add(origin);
        
        // 从原点开始向外扩散生成位置，但增加更多随机性
        BlockPos current = origin;
        for (int i = 1; i < count; i++) {
            // 在当前点的附近寻找空位，增加随机性
            BlockPos next = findNearbyPositionWithRandomness(current, positions, random);
            if (next != null) {
                positions.add(next);
                // 有时切换到已有的位置继续扩散，增加聚集感
                if (random.nextInt(3) == 0 && !positions.isEmpty()) {
                    current = positions.get(random.nextInt(positions.size()));
                } else {
                    current = next;
                }
            } else {
                // 如果找不到合适的位置，尝试从已有的位置中选择一个继续扩散
                if (!positions.isEmpty()) {
                    current = positions.get(random.nextInt(positions.size()));
                    next = findNearbyPositionWithRandomness(current, positions, random);
                    if (next != null) {
                        positions.add(next);
                        current = next;
                    }
                }
            }
        }
        
        return positions;
    }
    
    /**
     * 寻找附近的空位（增加随机性）
     * 
     * @param center 中心点
     * @param occupied 已占用位置
     * @param random 随机数生成器
     * @return 附近的空位，如果找不到则返回null
     */
    private BlockPos findNearbyPositionWithRandomness(BlockPos center, List<BlockPos> occupied, RandomSource random) {
        // 在中心点周围更大的范围内寻找，增加随机性
        for (int attempts = 0; attempts < 25; attempts++) {
            // 增加随机范围，使分布更加自然
            int dx = random.nextInt(5) - 2; // -2, -1, 0, 1, 2
            int dy = random.nextInt(5) - 2;
            int dz = random.nextInt(5) - 2;
            
            // 增加偏向性，使水晶簇更自然
            if (Math.abs(dx) + Math.abs(dy) + Math.abs(dz) > 4) {
                // 如果距离太远，有一定概率跳过
                if (random.nextInt(3) != 0) {
                    continue;
                }
            }
            
            BlockPos pos = center.offset(dx, dy, dz);
            
            // 检查位置是否已被占用
            if (occupied.contains(pos)) {
                continue;
            }
            
            // 检查位置是否为空并且附近有合适的附着面
            if (level.isEmptyBlock(pos) && hasValidAttachSurface(pos)) {
                return pos;
            }
        }
        
        return null;
    }
    
    /**
     * 检查位置附近是否有有效的附着面
     * 
     * @param pos 位置
     * @return 如果有有效的附着面返回true，否则返回false
     */
    private boolean hasValidAttachSurface(BlockPos pos) {
        // 检查六个方向是否有合适的附着面
        for (Direction direction : Direction.values()) {
            BlockPos attachPos = pos.relative(direction.getOpposite());
            BlockState attachState = level.getBlockState(attachPos);
            
            // 检查是否是石头或深层板岩
            if (attachState.is(BlockTags.STONE_ORE_REPLACEABLES) || 
                attachState.is(BlockTags.DEEPSLATE_ORE_REPLACEABLES)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 获取随机附着方向
     * 
     * @param level 世界
     * @param pos 位置
     * @param random 随机数生成器
     * @return 随机选择的有效附着方向
     */
    private Direction getRandomAttachDirection(WorldGenLevel level, BlockPos pos, RandomSource random) {
        // 检查六个方向是否有合适的附着面
        List<Direction> validDirections = new ArrayList<>();
        for (Direction direction : Direction.values()) {
            BlockPos attachPos = pos.relative(direction.getOpposite());
            BlockState attachState = level.getBlockState(attachPos);
            
            // 检查是否是石头或深层板岩
            if (attachState.is(BlockTags.STONE_ORE_REPLACEABLES) || 
                attachState.is(BlockTags.DEEPSLATE_ORE_REPLACEABLES)) {
                validDirections.add(direction);
            }
        }
        
        // 如果有有效的附着面，随机选择一个
        if (!validDirections.isEmpty()) {
            return validDirections.get(random.nextInt(validDirections.size()));
        }
        
        return null;
    }
    
    /**
     * 放置单个水晶
     * 
     * @param pos 位置
     * @param attachDirection 附着方向
     * @param crystalBlock 水晶方块
     * @param levelValue 水晶等级
     * @return 是否放置成功
     */
    private boolean placeCrystal(BlockPos pos, Direction attachDirection, 
                                CrystalBaseBlock crystalBlock, int levelValue) {
        try {
            // 设置方块状态
            BlockState state = crystalBlock.defaultBlockState()
                    .setValue(CrystalBaseBlock.FACING, attachDirection)
                    .setValue(crystalBlock.getLevelProperty(), levelValue);
            
            // 放置方块
            level.setBlock(pos, state, 2);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}